﻿using Dotnet.Utils.Utility.ObjectExtensions.Obj;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;

// ReSharper disable UnusedMember.Global
// ReSharper disable MemberCanBePrivate.Global

namespace Dotnet.Utils.Utility.AssemblyUtil
{
    /// <summary>
    /// Type扩展.
    /// </summary>
    public static class TypeExtension
    {
        public static readonly BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;

        //不包含父类
        public static readonly BindingFlags bindingCurrentAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly;

        public static string GetFullNameWithAssemblyName(this Type type)
        {
            return type.FullName + ", " + type.Assembly.GetName().Name;
        }

        /// <summary>
        /// 获取所有子类
        /// </summary>
        public static IEnumerable<Type> GetSubClass(IEnumerable<Type> types, Type baseType)
        {
            if (baseType.IsInterface)
            {
                return types.Where(type => baseType.IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract);
            }
            else if (baseType.IsAbstract)
            {
                return types.Where(type => type.IsSubclassOf(baseType) && !type.IsInterface && !type.IsAbstract);
            }
            else
            {
                return types.Where(type => type.IsSubclassOf(baseType));
            }
        }

        /// <summary>
        /// 获取类的所有方法
        /// </summary>
        /// <param name="type"></param>
        /// <param name="searchName"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<MethodInfo> GetAllMethodInfo(Type type, string searchName = "", bool inherit = true)
        {
            var bindingFlags = inherit ? bindingAttr : bindingCurrentAttr;
            if (string.IsNullOrEmpty(searchName))
            {
                return type.GetMethods(bindingFlags);
            }
            return type.GetMethods(bindingFlags).Where(m => m.Name.Contains(searchName));
        }

        /// <summary>
        /// 获取类的所有字段
        /// </summary>
        /// <param name="type"></param>
        /// <param name="searchName"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<FieldInfo> GetAllField(Type type, string searchName = "", bool inherit = true)
        {
            var bindingFlags = inherit ? bindingAttr : bindingCurrentAttr;
            if (string.IsNullOrEmpty(searchName))
            {
                return type.GetFields(bindingFlags);
            }
            return type.GetFields(bindingFlags).Where(m => m.Name.Contains(searchName));
        }

        /// <summary>
        /// 获取类的所有属性
        /// </summary>
        /// <param name="type"></param>
        /// <param name="searchName"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<PropertyInfo> GetAllPropertie(Type type, string searchName = "", bool inherit = true)
        {
            var bindingFlags = inherit ? bindingAttr : bindingCurrentAttr;
            if (string.IsNullOrEmpty(searchName))
            {
                return type.GetProperties(bindingFlags);
            }
            return type.GetProperties(bindingFlags).Where(m => m.Name.Contains(searchName));
        }

        /// <summary>
        /// 获取类的所有属性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<PropertyInfo> GetAllPropertie<T>(bool inherit = true) where T : new()
        {
            var bindingFlags = inherit ? bindingAttr : bindingCurrentAttr;

            var instanceOfT = Activator.CreateInstance<T>() ?? new T();

            PropertyInfo[] propertyInfos = instanceOfT.GetType().GetProperties(bindingFlags);

            return propertyInfos;
        }

        /// <summary>
        /// 获取类的所有属性名
        /// </summary>
        /// <param name="type"></param>
        /// <param name="searchName"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static List<string> GetAllPropertieNames(Type type, bool inherit = true)
        {
            var propertyInfos = GetAllPropertie(type, inherit: inherit);
            return propertyInfos.Select(a => a.Name).ToList();
        }

        /// <summary>
        /// 获取属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static T GetPropertyValue<T>(this object obj, string propertyName)
        {
            var property = obj.GetType().GetProperty(propertyName);
            if (property == null) return default;
            return (T)property.GetValue(obj);
        }

        /// <summary>
        /// 获取静态属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static T GetStaticPropertyValue<T>(this Type type, string propertyName)
        {
            var property = type.GetProperty(propertyName);
            if (property == null) return default;
            return (T)property.GetValue(null);
        }

        /// <summary>
        /// 确定是否可以将此类型的实例分配
        /// an instance of the <typeparamref name="TTarget"></typeparamref>.
        ///
        /// Internally uses <see cref="Type.IsAssignableFrom"/>.
        /// </summary>
        /// <typeparam name="TTarget">Target type</typeparam> (as reverse).
        public static bool IsAssignableTo<TTarget>([NotNull] this Type type)
        {
            return type.IsAssignableTo(typeof(TTarget));
        }

        /// <summary>
        /// 确定是否可以将此类型的实例分配
        /// an instance of the <paramref name="targetType"></paramref>.
        ///
        /// Internally uses <see cref="Type.IsAssignableFrom"/> (as reverse).
        /// </summary>
        /// <param name="type">this type</param>
        /// <param name="targetType">Target type</param>
        public static bool IsAssignableTo([NotNull] this Type type, [NotNull] Type targetType)
        {
            return targetType.IsAssignableFrom(type);
        }

        /// <summary>
        /// 获取此类型的所有基类。
        /// </summary>
        /// <param name="type">The type to get its base classes.</param>
        /// <param name="includeObject">True, to include the standard <see cref="object"/> type in the returned array.</param>
        public static Type[] GetBaseClasses([NotNull] this Type type, bool includeObject = true)
        {
            var types = new List<Type>();
            AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject);
            return types.ToArray();
        }

        /// <summary>
        /// 获取此类型的所有基类。
        /// </summary>
        /// <param name="type">The type to get its base classes.</param>
        /// <param name="stoppingType">A type to stop going to the deeper base classes. This type will be be included in the returned array</param>
        /// <param name="includeObject">True, to include the standard <see cref="object"/> type in the returned array.</param>
        public static Type[] GetBaseClasses([NotNull] this Type type, Type stoppingType, bool includeObject = true)
        {
            var types = new List<Type>();
            AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject, stoppingType);
            return types.ToArray();
        }

        private static void AddTypeAndBaseTypesRecursively(
            [NotNull] List<Type> types,
            Type? type,
            bool includeObject,
            Type? stoppingType = null)
        {
            if (type == null || type == stoppingType)
            {
                return;
            }

            if (!includeObject && type == typeof(object))
            {
                return;
            }

            AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject, stoppingType);
            types.Add(type);
        }

        /// <summary>
        /// 判断类型是否为Nullable类型
        /// </summary>
        /// <param name="type">要处理的类型</param>
        /// <returns>是返回True，不是返回False</returns>
        public static bool IsNullable(this Type type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);

        /// <summary>
        /// 判断当前类型是否可由指定类型派生
        /// </summary>
        /// <typeparam name="TBaseType"></typeparam>
        /// <param name="type"></param>
        /// <param name="canAbstract"></param>
        /// <returns></returns>
        public static bool IsDeriveClassFrom<TBaseType>(this Type type, bool canAbstract = false) => type.IsDeriveClassFrom(typeof(TBaseType), canAbstract);

        /// <summary>
        /// 判断当前类型是否可由指定类型派生
        /// </summary>
        public static bool IsDeriveClassFrom(this Type type, Type baseType, bool canAbstract = false)
        {
            type.NotNull(nameof(type));
            baseType.NotNull(nameof(baseType));
            return type.IsClass && !canAbstract && !type.IsAbstract && type.IsBaseOn(baseType);
        }

        /// <summary>
        /// 返回当前类型是否是指定基类的派生类
        /// </summary>
        /// <param name="type">当前类型</param>
        /// <param name="baseType">要判断的基类型</param>
        /// <returns></returns>
        public static bool IsBaseOn(this Type type, Type baseType) => baseType.IsGenericTypeDefinition ? type.HasImplementedRawGeneric(baseType) : baseType.IsAssignableFrom(type);

        /// <summary>
        /// 判断指定的类型 <paramref name="type" /> 是否是指定泛型类型的子类型，或实现了指定泛型接口。
        /// </summary>
        /// <param name="type">需要测试的类型。</param>
        /// <param name="generic">泛型接口类型，传入 typeof(IXxx&lt;&gt;)</param>
        /// <returns>如果是泛型接口的子类型，则返回 true，否则返回 false。</returns>
        public static bool HasImplementedRawGeneric(this Type type, Type generic)
        {
            type.NotNull(nameof(type));
            generic.NotNull(nameof(generic));
            // 测试接口。
            var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
            if (isTheRawGenericType) return true;
            // 测试类型。
            while (type != typeof(object))
            {
                isTheRawGenericType = IsTheRawGenericType(type);
                if (isTheRawGenericType) return true;
                type = type.BaseType!;
            }
            // 没有找到任何匹配的接口或类型。
            return false;

            // 测试某个类型是否是指定的原始接口。
            bool IsTheRawGenericType(Type test) => generic == (test.IsGenericType ? test.GetGenericTypeDefinition() : test);
        }

        /// <summary>
        /// 通过类型转换器获取Nullable类型的基础类型
        /// </summary>
        /// <param name="type"> 要处理的类型对象 </param>
        /// <returns> </returns>
        public static Type GetUnNullableType(this Type type)
        {
            if (!type.IsNullable()) return type;
            NullableConverter nullableConverter = new(type);
            return nullableConverter.UnderlyingType;
        }

        /// <summary>
        /// 是否是 ValueTuple
        /// </summary>
        /// <param name="type">type</param>
        /// <returns></returns>
        public static bool IsValueTuple(this Type type) => type.IsValueType && type.FullName?.StartsWith("System.ValueTuple`", StringComparison.Ordinal) == true;

        /// <summary>
        /// 判断是否基元类型，如果是可空类型会先获取里面的类型，如 int? 也是基元类型
        /// The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.
        /// </summary>
        /// <param name="type">type</param>
        /// <returns></returns>
        public static bool IsPrimitiveType(this Type type) => (Nullable.GetUnderlyingType(type) ?? type).IsPrimitive;

        /// <summary>
        /// Get ImplementedInterfaces
        /// </summary>
        /// <param name="type">type</param>
        /// <returns>当前类型实现的接口的集合。</returns>
        public static IEnumerable<Type> GetImplementedInterfaces(this Type type) => type.GetTypeInfo().ImplementedInterfaces;

        /// <summary>
        /// ToDescription
        /// </summary>
        /// <param name="type">type</param>
        /// <returns></returns>
        public static string ToDescription(this Type type) => type.GetCustomAttribute<DescriptionAttribute>()?.Description ?? string.Empty;

        /// <summary>
        /// 获取描述或者显示名称
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public static string ToDescription(this MemberInfo member)
        {
            var desc = member.GetCustomAttribute<DescriptionAttribute>();
            if (desc is not null) return desc.Description;
            //显示名
            var display = member.GetCustomAttribute<DisplayNameAttribute>();
            return display is not null ? display.DisplayName : member.Name;
        }

        /// <summary>
        /// 具有相匹配的通用类型
        /// </summary>
        /// <param name="interfaceType">接口类型</param>
        /// <param name="typeInfo">对象类型</param>
        /// <returns></returns>
        public static bool HasMatchingGenericArity(this Type interfaceType, TypeInfo typeInfo)
        {
            if (!typeInfo.IsGenericType) return true;
            var interfaceTypeInfo = interfaceType.GetTypeInfo();
            if (!interfaceTypeInfo.IsGenericType) return false;
            var argumentCount = interfaceType.GenericTypeArguments.Length;
            var parameterCount = typeInfo.GenericTypeParameters.Length;
            return argumentCount == parameterCount;
        }

        /// <summary>
        /// 获取注册类型
        /// </summary>
        /// <param name="interfaceType">接口类型.</param>
        /// <param name="typeInfo">对象类型</param>
        /// <returns></returns>
        public static Type GetRegistrationType(this Type interfaceType, TypeInfo typeInfo)
        {
            if (!typeInfo.IsGenericTypeDefinition) return interfaceType;
            var interfaceTypeInfo = interfaceType.GetTypeInfo();
            return interfaceTypeInfo.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType;
        }

        public static IEnumerable<Type> GetTypes<T>(Assembly[] assemblies)
        {
            List<Type> list = new List<Type>();
            foreach (Assembly assembly in assemblies)
            {
                Type[] types = assembly.GetTypes();
                foreach (Type type in types)
                {
                    if (!type.IsInterface && !type.IsAbstract && typeof(T).IsAssignableFrom(type))
                    {
                        list.Add(type);
                    }
                }
            }
            return list;
        }

        public static string GetFullTypeName(Type type)
        {
            return string.Format("{0}, {1}", type.FullName, type.Assembly.GetName().Name);
        }

        public static Type GetEnumerableType(Type type)
        {
            Type result = null;
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                result = type.GetGenericArguments()[0];
            }
            else
            {
                foreach (Type type2 in type.GetInterfaces())
                {
                    if (type2.IsGenericType && type2.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                    {
                        return type2.GetGenericArguments()[0];
                    }
                }
            }
            return result;
        }
    }
}